package com.progenies.ecg.asm31.model.factory;

import java.util.List;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

import com.progenies.ecg.asm31.model.codeparts.IConfigurableCodePart;
import com.progenies.ecg.asm31.model.codeparts.IConfigurableMethodObject;
import com.progenies.ecg.asm31.util.GenerationUtils;
import com.progenies.ecg.asm31.util.VariableRegister;
import com.progenies.ecg.generator.ClassGeneratorFactory;
import com.progenies.ecg.model.ClassObject;
import com.progenies.ecg.model.MethodObject;
import com.progenies.ecg.model.ParameterObject;

/**
 * ASM3.1 implementation of Method Object
 * @author ofc587
 *
 */
class MethodObjectASM31 extends MethodObject implements IConfigurableMethodObject
{
	private ClassVisitor visitor;
	private ClassObject parent;
	private VariableRegister varRegister;
	
	/**
	 * Creates a new public method with parameters
	 * @param exceptions 
	 */
	MethodObjectASM31(String name, ParameterObject<?> parameters[], Class<?> returnType, List<Class<? extends Throwable>> exceptions)
	{
		this.name=name;
		this.modifiers=new int[] {Opcodes.ACC_PUBLIC};
		this.parameters=parameters;
		this.returnType=returnType;
		this.setExceptions(exceptions); //use setter for copy of contents, not save a reference for the arrayList
	}

	/* (non-Javadoc)
	 * @see com.progenies.ecg.model.AbstractCodeObject#generateBytecode()
	 */
	/* (non-Javadoc)
	 * @see com.progenies.ecg.asm31.model.factory.IConfigurableMethodObject#generateBytecode()
	 */
	@Override
	public void generateBytecode() {
		
		if(this.visitor==null )
			throw new IllegalStateException("Method has not been configurated, please use ClassGenerator");
		
		MethodVisitor mv=visitor.visitMethod(GenerationUtils.getAccessTranslated(modifiers), name, GenerationUtils.translateDescription(this.parameters, this.returnType), null, GenerationUtils.getClassesNames(exceptions));
		mv.visitCode();
		
		//register parameters as local variables
		if(this.parameters!=null)
		{
			for(ParameterObject<?> param : parameters)
				varRegister.registerLocalVariable(param.getName(), Type.getType(param.getClassType()));
		}
		
		//if there is no code block, i'll invoke a default return
		if(this.code==null)
			this.code=ClassGeneratorFactory.getClassGenerator().lineFactory.methods.returnEmpty();
		else //i'll manage if the user forgot to add empty return
			GenerationUtils.checkHasReturn(this);

		
					
		//configure code block
		((IConfigurableCodePart)this.code).configure(mv, this, varRegister);
		
		//generate bytecode
		this.code.generateBytecode();
		
		//ends method
		//auto calculate stack/frames by ASM
		mv.visitMaxs(Short.MAX_VALUE, Short.MAX_VALUE);
		mv.visitEnd();
	}
	
	/* (non-Javadoc)
	 * @see com.progenies.ecg.asm31.model.factory.IConfigurableMethodObject#configure(org.objectweb.asm.ClassVisitor, com.progenies.ecg.asm31.model.factory.IConfigurableClassObject, com.progenies.ecg.asm31.util.VariableRegister)
	 */
	@Override
	public void configure(ClassVisitor visitor,	ClassObject parent, VariableRegister varRegister) {
		this.visitor=visitor;
		this.parent=parent;
		this.varRegister=varRegister;
	}

	/* (non-Javadoc)
	 * @see com.progenies.ecg.asm31.model.factory.IConfigurableMethodObject#getParent()
	 */
	@Override
	public ClassObject getParent() {
		return parent;
	}

}
